package com.generalbackend.config;

import com.generalbackend.role.filter.SecurityGlobalFilter;
import com.generalbackend.role.filter.WebApiPermissionFilter;
import com.generalbackend.role.service.IRestApiService;
import com.generalbackend.role.util.RoleConf;
import com.generalbackend.handler.LoginFailureHandler;
import com.generalbackend.handler.LoginSuccessHandler;
import com.generalbackend.service.SecurityUserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author tim.xie
 * @date 2021/11/29 16:38
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private IRestApiService restApiService;
    @Autowired
    private WebApiPermissionFilter webApiPermissionFilter;
    @Autowired
    private SecurityGlobalFilter securityGlobalFilter;

    @Bean
    UserDetailsService userService() {
        return new SecurityUserServiceImpl();
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关闭csrf防护
        http.csrf().disable();
        http.headers().frameOptions().disable();
        RequestMatcher[] requestMatchers = new RequestMatcher[2];
        requestMatchers[0] = new AntPathRequestMatcher("/index.html", null);
        requestMatchers[1] = new CommonRoleRequestMatcher();
//        //表单提交
//        http.formLogin()
//                // 自定义登录页面
//                .loginPage("/api/toLogin")
//                // 必须和表单提交的接口路径一致，会去执行自定义登录逻辑
//                .loginProcessingUrl("/login")
//                // 登录成功后跳转到的页面，只接受post请求
//                .successForwardUrl("/api/toHome")
//                .successHandler(loginSuccessHandler())
//                // 登录失败后跳转的页面
//                .failureForwardUrl("/api/failureLogin");
//        http.authorizeRequests()
//                .requestMatchers(requestMatchers).permitAll()
//                .anyRequest().authenticated();
        http.authorizeRequests()
                .requestMatchers(requestMatchers).permitAll()
                .anyRequest().authenticated()
                .and()
                .cors()
                .and()
                .formLogin()
                .loginProcessingUrl("/api/login")
                .loginPage("/api/toLogin")
                .successHandler(loginSuccessHandler())
                .failureHandler(loginFailureHandler())
                .and()
                .logout()
                .permitAll()
                .and()
                .sessionManagement()
                .maximumSessions(5000)
                .expiredUrl("/index.html#/login")
                .sessionRegistry(sessionRegistry());
        // 全局过滤器
        http.addFilterBefore(securityGlobalFilter, SecurityContextPersistenceFilter.class);
        // 接口权限过滤器
        http.addFilterBefore(webApiPermissionFilter, ExceptionTranslationFilter.class);
    }

    @Bean
    public PasswordEncoder pw(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 不登录即可访问的名单
     */
    private class CommonRoleRequestMatcher implements RequestMatcher {
        @Override
        public boolean matches(HttpServletRequest httpServletRequest) {
            String uri = httpServletRequest.getServletPath();
            List<String> commonUriByRole = Arrays.asList(RoleConf.getPermissionUrl("common").split(","));
            List<String> commonUriByPermission = restApiService.getCommonApi().stream().map(api -> api.getUrl()).collect(Collectors.toList());
            return WebApiPermissionFilter.apiInStr(uri, commonUriByRole) ||
                    WebApiPermissionFilter.apiInStr(uri, commonUriByPermission);
        }
    }

    @Bean
    public LoginSuccessHandler loginSuccessHandler() {
        return new LoginSuccessHandler();
    }

    @Bean
    public LoginFailureHandler loginFailureHandler() {
        return new LoginFailureHandler();
    }

    @Bean
    public FilterRegistrationBean registration(WebApiPermissionFilter webApiPermissionFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(webApiPermissionFilter);
        registration.setEnabled(false);
        return registration;
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));
        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
        configuration.setAllowCredentials(true);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}
